﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.Tasks;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 使用指定的 Separable Kernel 進行 Correlation 運算的點陣圖濾鏡工作。
	/// </summary>
	public abstract class SeparableKernelCorrelationFilterTask : BitmapImageFilterTask
	{
		#region ProgressInfo
		class ProgressInfo
		{
			public volatile int CompletedRows;
			public readonly bool Has2DKernels;
			public volatile bool HasError;
			public readonly int TotalRows;
			#region 建構子
			public ProgressInfo(int totalRows, bool has2DKernels)
			{
				this.TotalRows = totalRows;
				this.Has2DKernels = has2DKernels;
			}
			#endregion
		}
        #endregion
		#region 常數
		const int PROCESSING_CHUNK_SIZE = 2000000;
        #endregion
		#region 欄位
		BitmapImageFilterChannels _Channels = BitmapImageFilterChannels.Argb;
		SeparableCorrelationKernels _Kernels;
        #endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 SeparableKernelCorrelationFilterTask 執行個體。
		/// </summary>
		protected SeparableKernelCorrelationFilterTask()
		{ }
		/// <summary>
		/// 初始化新的 SeparableKernelCorrelationFilterTask 執行個體。
		/// </summary>
		/// <param name="sourceImage">來源點陣圖。</param>
		protected SeparableKernelCorrelationFilterTask(IBitmapImage sourceImage)
			: base(sourceImage)
		{ }
        #endregion
		#region ApplyFilter
		/// <summary>
		/// 套用濾鏡至點陣圖。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="filterData">套用濾鏡所需的資料。</param>
		/// <returns>True 表示濾鏡套用成功，False 表示失敗。</returns>
		protected override unsafe bool ApplyFilter(TaskVersion version, BitmapImageFilterData filterData)
		{
			//取得資料
			SeparableCorrelationKernels kernels;
			BitmapImageFilterChannels channels;
			if (!this.ReadValue<SeparableCorrelationKernels>(version, ref _Kernels, out kernels)
				|| !this.ReadValue<BitmapImageFilterChannels>(version, ref _Channels, out channels))
			{
				return true;
			}

			//確認是否需套用濾鏡
			bool hasHorzKernels = false;
			bool hasVertKernels = false;
			if (kernels != null)
			{
				if (channels != BitmapImageFilterChannels.Luminance)
				{
					if ((channels & BitmapImageFilterChannels.Alpha) != 0)
					{
						hasHorzKernels = (kernels.HorizontalAlphaKernel != null);
						hasVertKernels = (kernels.VerticalAlphaKernel != null);
					}
					else
					{
						kernels.HorizontalAlphaKernel = null;
						kernels.VerticalAlphaKernel = null;
					}
					if ((channels & BitmapImageFilterChannels.Red) != 0)
					{
						hasHorzKernels |= (kernels.HorizontalRedKernel != null);
						hasVertKernels |= (kernels.VerticalRedKernel != null);
					}
					else
					{
						kernels.HorizontalRedKernel = null;
						kernels.VerticalRedKernel = null;
					}
					if ((channels & BitmapImageFilterChannels.Green) != 0)
					{
						hasHorzKernels |= (kernels.HorizontalGreenKernel != null);
						hasVertKernels |= (kernels.VerticalGreenKernel != null);
					}
					else
					{
						kernels.HorizontalGreenKernel = null;
						kernels.VerticalGreenKernel = null;
					}
					if ((channels & BitmapImageFilterChannels.Blue) != 0)
					{
						hasHorzKernels |= (kernels.HorizontalBlueKernel != null);
						hasVertKernels |= (kernels.VerticalBlueKernel != null);
					}
					else
					{
						kernels.HorizontalBlueKernel = null;
						kernels.VerticalBlueKernel = null;
					}
				}
				else
				{
					hasHorzKernels = (kernels.HorizontalLuminanceKernel != null);
					hasVertKernels = (kernels.VerticalLuminanceKernel != null);
				}
			}
			if (!hasHorzKernels && !hasVertKernels)
				return true;

			//計算平行狀態
			int maxParallelCount = Environment.ProcessorCount;
			int parallelCount = Math.Min(maxParallelCount, (int)(Math.Ceiling((filterData.PixelWidth * filterData.PixelHeight) / (double)PROCESSING_CHUNK_SIZE) + 0.1));
			int chunkHeight = (int)(Math.Ceiling((double)filterData.PixelHeight / parallelCount) + 0.1);

			//水平方向運算
			SemaphoreSlim barrier = new SemaphoreSlim(0);
			ProgressInfo progressInfo = new ProgressInfo(filterData.PixelHeight, (hasHorzKernels | hasVertKernels));
			if (hasHorzKernels)
			{
				//平行套用濾鏡
				for (int i = 0, start = 0; i < parallelCount; ++i, start += chunkHeight)
				{
					int startRow = start;
					int endRow = Math.Min(filterData.PixelHeight, startRow + chunkHeight);
					ThreadPool.QueueUserWorkItem(new WaitCallback((state) =>
					{
						if (channels != BitmapImageFilterChannels.Luminance)
							this.ApplyHorizontalArgbFilter(version, filterData, startRow, endRow, kernels, progressInfo, barrier);
						else
							this.ApplyHorizontalLuminanceFilter(version, filterData, startRow, endRow, kernels, progressInfo, barrier);
					}));
				}
				for (int i = parallelCount; i > 0; --i)
					barrier.Wait();

				//中斷確認
				if (this.IsCanceling)
					return true;
				if (progressInfo.HasError)
					return false;
			}

			//垂直方向運算
			if (hasVertKernels)
			{
				//平行套用濾鏡
				progressInfo.CompletedRows = 0;
				for (int i = 0, start = 0; i < parallelCount; ++i, start += chunkHeight)
				{
					int startRow = start;
					int endRow = Math.Min(filterData.PixelHeight, startRow + chunkHeight);
					ThreadPool.QueueUserWorkItem(new WaitCallback((state) =>
					{
						if (channels != BitmapImageFilterChannels.Luminance)
							this.ApplyVerticalArgbFilter(version, filterData, startRow, endRow, kernels, progressInfo, barrier);
						else
							this.ApplyVerticalLuminanceFilter(version, filterData, startRow, endRow, kernels, progressInfo, barrier);
					}));
				}
				for (int i = parallelCount; i > 0; --i)
					barrier.Wait();

				//中斷確認
				if (this.IsCanceling)
					return true;
				if (progressInfo.HasError)
					return false;
			}

			//完成
			return true;
		}
		#endregion
		#region ApplyHorizontalArgbFilter
		unsafe void ApplyHorizontalArgbFilter(TaskVersion version, BitmapImageFilterData filterData, int startRow, int endRow, SeparableCorrelationKernels kernels, ProgressInfo progressInfo, SemaphoreSlim barrier)
		{
			try
			{
				//取得像素格式
				int bytesPerPixel = filterData.BytesPerPixel;
				bool includeAlpha = filterData.IsAlphaIncluded;
				bool is16BitsColor = filterData.Is16BitsColor;

				//取得 Kernel
				double[] aKernel = kernels.HorizontalAlphaKernel;
				double[] rKernel = kernels.HorizontalRedKernel;
				double[] gKernel = kernels.HorizontalGreenKernel;
				double[] bKernel = kernels.HorizontalBlueKernel;
				int kernelSize = 0;
				if (aKernel != null)
					kernelSize = aKernel.Length;
				else if (rKernel != null)
					kernelSize = rKernel.Length;
				else if (gKernel != null)
					kernelSize = gKernel.Length;
				else
					kernelSize = bKernel.Length;
				int halfKernelSize = (kernelSize / 2);

				//確認狀態
				if (kernelSize == 0)
				{
					lock (progressInfo)
					{
						progressInfo.HasError = true;
					}
					return;
				}

				//套用濾鏡
				int rowCount = 0;
				int pixelWidth = filterData.PixelWidth;
				int stride = filterData.Stride;
				byte* srcLine = ((byte*)filterData.SourcePixelBuffer + (stride * startRow));
				byte* resultLine = ((byte*)filterData.ResultPixelBuffer + (stride * startRow));
				for (int y = startRow; y < endRow; ++y, srcLine += stride, resultLine += stride)
				{
					//套用濾鏡
					byte* resultPixel = resultLine;
					for (int x = 0; x < pixelWidth; ++x, resultPixel += bytesPerPixel)
					{
						//套用 Kernel
						double r = 0, g = 0, b = 0, a = 0;
						for (int k = 0, kx = (x - halfKernelSize); k < kernelSize; ++k, ++kx)
						{
							byte* srcPixel;
							if (kx < 0)
								srcPixel = (srcLine - (bytesPerPixel * kx));
							else if (kx >= pixelWidth)
								srcPixel = (srcLine + (bytesPerPixel * ((pixelWidth * 2) - kx - 1)));
							else
								srcPixel = (srcLine + (bytesPerPixel * kx));
							if (!is16BitsColor)
							{
								if (bKernel != null)
									b += (*(srcPixel) * bKernel[k]);
								if (gKernel != null)
									g += (*(srcPixel + 1) * gKernel[k]);
								if (rKernel != null)
									r += (*(srcPixel + 2) * rKernel[k]);
								if (aKernel != null && includeAlpha)
									a += (*(srcPixel + 3) * aKernel[k]);
							}
							else
							{
								if (rKernel != null)
									r += (*(ushort*)(srcPixel) * rKernel[k]);
								if (gKernel != null)
									g += (*(ushort*)(srcPixel + 2) * gKernel[k]);
								if (bKernel != null)
									b += (*(ushort*)(srcPixel + 4) * bKernel[k]);
								if (aKernel != null && includeAlpha)
									a += (*(ushort*)(srcPixel + 6) * aKernel[k]);
							}
						}

						//儲存結果
						if (!is16BitsColor)
						{
							if (bKernel != null)
							{
								if (b < 0)
									b = 0.1;
								else if (b > 255)
									b = 254.9;
								*(resultPixel) = (byte)(b + 0.5);
							}
							if (gKernel != null)
							{
								if (g < 0)
									g = 0.1;
								else if (g > 255)
									g = 254.9;
								*(resultPixel + 1) = (byte)(g + 0.5);
							}
							if (rKernel != null)
							{
								if (r < 0)
									r = 0.1;
								else if (r > 255)
									r = 254.9;
								*(resultPixel + 2) = (byte)(r + 0.5);
							}
							if (aKernel != null && includeAlpha)
							{
								if (a < 0)
									a = 0.1;
								else if (a > 255)
									a = 254.9;
								*(resultPixel + 3) = (byte)(a + 0.5);
							}
						}
						else
						{
							if (rKernel != null)
							{
								if (r < 0)
									r = 0.1;
								else if (r > 65535)
									r = 65534.9;
								*(ushort*)(resultPixel) = (ushort)(r + 0.5);
							}
							if (gKernel != null)
							{
								if (g < 0)
									g = 0.1;
								else if (g > 65535)
									g = 65534.9;
								*(ushort*)(resultPixel + 2) = (ushort)(g + 0.5);
							}
							if (bKernel != null)
							{
								if (b < 0)
									b = 0.1;
								else if (b > 65535)
									b = 65534.9;
								*(ushort*)(resultPixel + 4) = (ushort)(b + 0.5);
							}
							if (aKernel != null && includeAlpha)
							{
								if (a < 0)
									a = 0.1;
								else if (a > 65535)
									a = 65534.9;
								*(ushort*)(resultPixel + 6) = (ushort)(a + 0.5);
							}
						}
					}

					//更新進度
					++rowCount;
					if (rowCount == 100)
					{
						lock (progressInfo)
						{
							progressInfo.CompletedRows += rowCount;
							rowCount = 0;
							this.ReportProgress(version, (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
							if (this.IsCanceling)
								break;
						}
					}
				}

				//更新進度
				if (rowCount > 0)
				{
					lock (progressInfo)
					{
						progressInfo.CompletedRows += rowCount;
						this.ReportProgress(version, (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
					}
				}
			}
			catch
			{
				lock (progressInfo)
				{
					progressInfo.HasError = true;
				}
			}
			finally
			{
				barrier.Release();
			}
		}
        #endregion
		#region ApplyHorizontalLuminanceFilter
		unsafe void ApplyHorizontalLuminanceFilter(TaskVersion version, BitmapImageFilterData filterData, int startRow, int endRow, SeparableCorrelationKernels kernels, ProgressInfo progressInfo, SemaphoreSlim barrier)
		{
			try
			{
				//取得像素格式
				int bytesPerPixel = filterData.BytesPerPixel;
				bool includeAlpha = filterData.IsAlphaIncluded;
				bool is16BitsColor = filterData.Is16BitsColor;

				//取得 Kernel
				double[] kernel = kernels.HorizontalLuminanceKernel;
				int kernelSize = kernel.Length;
				int halfKernelSize = (kernelSize / 2);

				//套用濾鏡
				int rowCount = 0;
				int pixelWidth = filterData.PixelWidth;
				int stride = filterData.Stride;
				byte* srcLine = ((byte*)filterData.SourcePixelBuffer + (stride * startRow));
				byte* resultLine = ((byte*)filterData.ResultPixelBuffer + (stride * startRow));
				for (int y = startRow; y < endRow; ++y, srcLine += stride, resultLine += stride)
				{
					//套用濾鏡
					byte* resultPixel = resultLine;
					for (int x = 0; x < pixelWidth; ++x, resultPixel += bytesPerPixel)
					{
						//套用 Kernel
						double newLuminance = 0;
						for (int k = 0, kx = (x - halfKernelSize); k < kernelSize; ++k, ++kx)
						{
							byte* srcPixel;
							if (kx < 0)
								srcPixel = (srcLine - (bytesPerPixel * kx));
							else if (kx >= pixelWidth)
								srcPixel = (srcLine + (bytesPerPixel * ((pixelWidth * 2) - kx - 1)));
							else
								srcPixel = (srcLine + (bytesPerPixel * kx));
							int r, g, b;
							if (!is16BitsColor)
							{
								b = *(srcPixel);
								g = *(srcPixel + 1);
								r = *(srcPixel + 2);
							}
							else
							{
								r = *(ushort*)(srcPixel);
								g = *(ushort*)(srcPixel + 2);
								b = *(ushort*)(srcPixel + 4);
							}
							newLuminance += (ColorConversion.ToLuminanceUnsafe(r, g, b) * kernel[k]);
						}

						//儲存結果
						if (!is16BitsColor)
						{
							int b = *(resultPixel);
							int g = *(resultPixel + 1);
							int r = *(resultPixel + 2);
							double luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
							if (luminance > 0)
							{
								double luminanceRatio = (newLuminance / luminance);
								r = (int)(luminanceRatio * r);
								g = (int)(luminanceRatio * g);
								b = (int)(luminanceRatio * b);
							}
							else
								r = g = b = (int)newLuminance;
							*(resultPixel) = (byte)(b < 0 ? 0 : (b > 255 ? 255 : b));
							*(resultPixel + 1) = (byte)(g < 0 ? 0 : (g > 255 ? 255 : g));
							*(resultPixel + 2) = (byte)(r < 0 ? 0 : (r > 255 ? 255 : r));
						}
						else
						{
							int r = *(ushort*)(resultPixel);
							int g = *(ushort*)(resultPixel + 2);
							int b = *(ushort*)(resultPixel + 4);
							double luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
							if (luminance > 0)
							{
								double luminanceRatio = (newLuminance / luminance);
								r = (int)(luminanceRatio * r);
								g = (int)(luminanceRatio * g);
								b = (int)(luminanceRatio * b);
							}
							else
								r = g = b = (int)newLuminance;
							*(ushort*)(resultPixel) = (ushort)(b < 0 ? 0 : (b > 65535 ? 65535 : b));
							*(ushort*)(resultPixel + 2) = (ushort)(g < 0 ? 0 : (g > 65535 ? 65535 : g));
							*(ushort*)(resultPixel + 4) = (ushort)(r < 0 ? 0 : (r > 65535 ? 65535 : r));
						}
					}

					//更新進度
					++rowCount;
					if (rowCount == 100)
					{
						lock (progressInfo)
						{
							progressInfo.CompletedRows += rowCount;
							rowCount = 0;
							this.ReportProgress(version, (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
							if (this.IsCanceling)
								break;
						}
					}
				}

				//更新進度
				if (rowCount > 0)
				{
					lock (progressInfo)
					{
						progressInfo.CompletedRows += rowCount;
						this.ReportProgress(version, (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
					}
				}
			}
			catch
			{
				lock (progressInfo)
				{
					progressInfo.HasError = true;
				}
			}
			finally
			{
				barrier.Release();
			}
		}
		#endregion
		#region ApplyVerticalArgbFilter
		unsafe void ApplyVerticalArgbFilter(TaskVersion version, BitmapImageFilterData filterData, int startRow, int endRow, SeparableCorrelationKernels kernels, ProgressInfo progressInfo, SemaphoreSlim barrier)
		{
			try
			{
				//取得像素格式
				int bytesPerPixel = filterData.BytesPerPixel;
				bool includeAlpha = filterData.IsAlphaIncluded;
				bool is16BitsColor = filterData.Is16BitsColor;

				//取得 Kernel
				double[] aKernel = kernels.VerticalAlphaKernel;
				double[] rKernel = kernels.VerticalRedKernel;
				double[] gKernel = kernels.VerticalGreenKernel;
				double[] bKernel = kernels.VerticalBlueKernel;
				int kernelSize = 0;
				if (aKernel != null)
					kernelSize = aKernel.Length;
				else if (rKernel != null)
					kernelSize = rKernel.Length;
				else if (gKernel != null)
					kernelSize = gKernel.Length;
				else
					kernelSize = bKernel.Length;
				int halfKernelSize = (kernelSize / 2);

				//確認狀態
				if (kernelSize == 0)
				{
					lock (progressInfo)
					{
						progressInfo.HasError = true;
					}
					return;
				}

				//套用濾鏡
				int rowCount = 0;
				int pixelHeight = filterData.PixelHeight;
				int pixelWidth = filterData.PixelWidth;
				int stride = filterData.Stride;
				byte* resultLine = ((byte*)filterData.ResultPixelBuffer + (stride * startRow));
				for (int y = startRow; y < endRow; ++y, resultLine += stride)
				{
					//套用濾鏡
					byte* baseSrcPixel = (byte*)filterData.ResultPixelBuffer;
					byte* resultPixel = resultLine;
					for (int x = 0; x < pixelWidth; ++x, baseSrcPixel += bytesPerPixel, resultPixel += bytesPerPixel)
					{
						//套用 Kernel
						double r = 0, g = 0, b = 0, a = 0;
						for (int k = 0, ky = (y - halfKernelSize); k < kernelSize; ++k, ++ky)
						{
							byte* srcPixel;
							if (ky < 0)
								srcPixel = (baseSrcPixel - (stride * ky));
							else if (ky >= pixelHeight)
								srcPixel = (baseSrcPixel + (stride * ((pixelHeight * 2) - ky - 1)));
							else
								srcPixel = (baseSrcPixel + (stride * ky));
							if (!is16BitsColor)
							{
								if (bKernel != null)
									b += (*(srcPixel) * bKernel[k]);
								if (gKernel != null)
									g += (*(srcPixel + 1) * gKernel[k]);
								if (rKernel != null)
									r += (*(srcPixel + 2) * rKernel[k]);
								if (aKernel != null && includeAlpha)
									a += (*(srcPixel + 3) * aKernel[k]);
							}
							else
							{
								if (rKernel != null)
									r += (*(ushort*)(srcPixel) * rKernel[k]);
								if (gKernel != null)
									g += (*(ushort*)(srcPixel + 2) * gKernel[k]);
								if (bKernel != null)
									b += (*(ushort*)(srcPixel + 4) * bKernel[k]);
								if (aKernel != null && includeAlpha)
									a += (*(ushort*)(srcPixel + 6) * aKernel[k]);
							}
						}

						//儲存結果
						if (!is16BitsColor)
						{
							if (bKernel != null)
							{
								if (b < 0)
									b = 0.1;
								else if (b > 255)
									b = 254.9;
								*(resultPixel) = (byte)(b + 0.5);
							}
							if (gKernel != null)
							{
								if (g < 0)
									g = 0.1;
								else if (g > 255)
									g = 254.9;
								*(resultPixel + 1) = (byte)(g + 0.5);
							}
							if (rKernel != null)
							{
								if (r < 0)
									r = 0.1;
								else if (r > 255)
									r = 254.9;
								*(resultPixel + 2) = (byte)(r + 0.5);
							}
							if (aKernel != null && includeAlpha)
							{
								if (a < 0)
									a = 0.1;
								else if (a > 255)
									a = 254.9;
								*(resultPixel + 3) = (byte)(a + 0.5);
							}
						}
						else
						{
							if (rKernel != null)
							{
								if (r < 0)
									r = 0.1;
								else if (r > 65535)
									r = 65534.9;
								*(ushort*)(resultPixel) = (ushort)(r + 0.5);
							}
							if (gKernel != null)
							{
								if (g < 0)
									g = 0.1;
								else if (g > 65535)
									g = 65534.9;
								*(ushort*)(resultPixel + 2) = (ushort)(g + 0.5);
							}
							if (bKernel != null)
							{
								if (b < 0)
									b = 0.1;
								else if (b > 65535)
									b = 65534.9;
								*(ushort*)(resultPixel + 4) = (ushort)(b + 0.5);
							}
							if (aKernel != null && includeAlpha)
							{
								if (a < 0)
									a = 0.1;
								else if (a > 65535)
									a = 65534.9;
								*(ushort*)(resultPixel + 6) = (ushort)(a + 0.5);
							}
						}
					}

					//更新進度
					++rowCount;
					if (rowCount == 100)
					{
						lock (progressInfo)
						{
							progressInfo.CompletedRows += rowCount;
							rowCount = 0;
							this.ReportProgress(version, (progressInfo.Has2DKernels ? 50 : 0) + (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
							if (this.IsCanceling)
								break;
						}
					}
				}

				//更新進度
				if (rowCount > 0)
				{
					lock (progressInfo)
					{
						progressInfo.CompletedRows += rowCount;
						this.ReportProgress(version, (progressInfo.Has2DKernels ? 50 : 0) + (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
					}
				}
			}
			catch
			{
				lock (progressInfo)
				{
					progressInfo.HasError = true;
				}
			}
			finally
			{
				barrier.Release();
			}
		}
		#endregion
		#region ApplyVerticalLuminanceFilter
		unsafe void ApplyVerticalLuminanceFilter(TaskVersion version, BitmapImageFilterData filterData, int startRow, int endRow, SeparableCorrelationKernels kernels, ProgressInfo progressInfo, SemaphoreSlim barrier)
		{
			try
			{
				//取得像素格式
				int bytesPerPixel = filterData.BytesPerPixel;
				bool includeAlpha = filterData.IsAlphaIncluded;
				bool is16BitsColor = filterData.Is16BitsColor;

				//取得 Kernel
				double[] kernel = kernels.VerticalLuminanceKernel;
				int kernelSize = kernel.Length;
				int halfKernelSize = (kernelSize / 2);

				//套用濾鏡
				int rowCount = 0;
				int pixelHeight = filterData.PixelHeight;
				int pixelWidth = filterData.PixelWidth;
				int stride = filterData.Stride;
				byte* resultLine = ((byte*)filterData.ResultPixelBuffer + (stride * startRow));
				for (int y = startRow; y < endRow; ++y, resultLine += stride)
				{
					//套用濾鏡
					byte* baseSrcPixel = (byte*)filterData.ResultPixelBuffer;
					byte* resultPixel = resultLine;
					for (int x = 0; x < pixelWidth; ++x, baseSrcPixel += bytesPerPixel, resultPixel += bytesPerPixel)
					{
						//套用 Kernel
						double newLuminance = 0;
						for (int k = 0, ky = (y - halfKernelSize); k < kernelSize; ++k, ++ky)
						{
							byte* srcPixel;
							if (ky < 0)
								srcPixel = (baseSrcPixel - (stride * ky));
							else if (ky >= pixelHeight)
								srcPixel = (baseSrcPixel + (stride * ((pixelHeight * 2) - ky - 1)));
							else
								srcPixel = (baseSrcPixel + (stride * ky));
							int r, g, b;
							if (!is16BitsColor)
							{
								b = *(srcPixel);
								g = *(srcPixel + 1);
								r = *(srcPixel + 2);
							}
							else
							{
								r = *(ushort*)(srcPixel);
								g = *(ushort*)(srcPixel + 2);
								b = *(ushort*)(srcPixel + 4);
							}
							newLuminance += (ColorConversion.ToLuminanceUnsafe(r, g, b) * kernel[k]);
						}

						//儲存結果
						if (!is16BitsColor)
						{
							int b = *(resultPixel);
							int g = *(resultPixel + 1);
							int r = *(resultPixel + 2);
							double luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
							if (luminance > 0)
							{
								double luminanceRatio = (newLuminance / luminance);
								r = (int)(luminanceRatio * r);
								g = (int)(luminanceRatio * g);
								b = (int)(luminanceRatio * b);
							}
							else
								r = g = b = (int)newLuminance;
							*(resultPixel) = (byte)(b < 0 ? 0 : (b > 255 ? 255 : b));
							*(resultPixel + 1) = (byte)(g < 0 ? 0 : (g > 255 ? 255 : g));
							*(resultPixel + 2) = (byte)(r < 0 ? 0 : (r > 255 ? 255 : r));
						}
						else
						{
							int r = *(ushort*)(resultPixel);
							int g = *(ushort*)(resultPixel + 2);
							int b = *(ushort*)(resultPixel + 4);
							double luminance = ColorConversion.ToLuminanceUnsafe(r, g, b);
							if (luminance > 0)
							{
								double luminanceRatio = (newLuminance / luminance);
								r = (int)(luminanceRatio * r);
								g = (int)(luminanceRatio * g);
								b = (int)(luminanceRatio * b);
							}
							else
								r = g = b = (int)newLuminance;
							*(ushort*)(resultPixel) = (ushort)(b < 0 ? 0 : (b > 65535 ? 65535 : b));
							*(ushort*)(resultPixel + 2) = (ushort)(g < 0 ? 0 : (g > 65535 ? 65535 : g));
							*(ushort*)(resultPixel + 4) = (ushort)(r < 0 ? 0 : (r > 65535 ? 65535 : r));
						}
					}

					//更新進度
					++rowCount;
					if (rowCount == 100)
					{
						lock (progressInfo)
						{
							progressInfo.CompletedRows += rowCount;
							rowCount = 0;
							this.ReportProgress(version, (progressInfo.Has2DKernels ? 50 : 0) + (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
							if (this.IsCanceling)
								break;
						}
					}
				}

				//更新進度
				if (rowCount > 0)
				{
					lock (progressInfo)
					{
						progressInfo.CompletedRows += rowCount;
						this.ReportProgress(version, (progressInfo.Has2DKernels ? 50 : 0) + (((double)progressInfo.CompletedRows / progressInfo.TotalRows) * (progressInfo.Has2DKernels ? 50 : 100)));
					}
				}
			}
			catch
			{
				lock (progressInfo)
				{
					progressInfo.HasError = true;
				}
			}
			finally
			{
				barrier.Release();
			}
		}
		#endregion
		#region Channels
		/// <summary>
		/// 取得或設定套用濾鏡的色彩組合，預設值為 BitmapImageFilterChannels.Argb。
		/// </summary>
		public BitmapImageFilterChannels Channels
		{
			get
			{
				return _Channels;
			}
			set
			{
				this.SetInitializingParameter<BitmapImageFilterChannels>(ref _Channels, value);
			}
		}
        #endregion
		#region Kernels
		/// <summary>
		/// 取得或設定進行濾鏡運算的 Separable Kernel 組合。
		/// </summary>
		protected SeparableCorrelationKernels Kernels
		{
			get
			{
				SeparableCorrelationKernels kernels = _Kernels;
				return (kernels != null ? kernels.Clone() : null);
			}
			set
			{
				lock (this.SyncRoot)
				{
					if (value != null)
						_Kernels = value.Clone();
					else
						_Kernels = null;
				}
			}
		}
        #endregion
		#region ValidateInitParameters
		/// <summary>
		/// 驗證初始化參數是否正確。
		/// </summary>
		/// <returns>True 表示參數正確，False 表示不正確。</returns>
		protected override bool ValidateInitParameters()
		{
			//呼叫基底
			if (!base.ValidateInitParameters())
				return false;

			//驗證 Channel
			if ((_Channels & BitmapImageFilterChannels.Luminance) != 0 && _Channels != BitmapImageFilterChannels.Luminance)
				return false;

			//驗證 Kernel
			int horzKernelSize = -1, vertKernelSize = -1;
			if (_Kernels != null)
			{
				if (_Channels != BitmapImageFilterChannels.Luminance)
				{
					if ((_Channels & BitmapImageFilterChannels.Alpha) != 0)
					{
						if (_Kernels.HorizontalAlphaKernel != null)
							horzKernelSize = _Kernels.HorizontalAlphaKernel.Length;
						if (_Kernels.VerticalAlphaKernel != null)
							vertKernelSize = _Kernels.VerticalAlphaKernel.Length;
					}
					if ((_Channels & BitmapImageFilterChannels.Red) != 0)
					{
						if (_Kernels.HorizontalRedKernel != null)
						{
							if (horzKernelSize >= 0 && horzKernelSize != _Kernels.HorizontalRedKernel.Length)
								return false;
							horzKernelSize = _Kernels.HorizontalRedKernel.Length;
						}
						if (_Kernels.VerticalRedKernel != null)
						{
							if (vertKernelSize >= 0 && vertKernelSize != _Kernels.VerticalRedKernel.Length)
								return false;
							vertKernelSize = _Kernels.VerticalRedKernel.Length;
						}
					}
					if ((_Channels & BitmapImageFilterChannels.Green) != 0)
					{
						if (_Kernels.HorizontalGreenKernel != null)
						{
							if (horzKernelSize >= 0 && horzKernelSize != _Kernels.HorizontalGreenKernel.Length)
								return false;
							horzKernelSize = _Kernels.HorizontalGreenKernel.Length;
						}
						if (_Kernels.VerticalGreenKernel != null)
						{
							if (vertKernelSize >= 0 && vertKernelSize != _Kernels.VerticalGreenKernel.Length)
								return false;
							vertKernelSize = _Kernels.VerticalGreenKernel.Length;
						}
					}
					if ((_Channels & BitmapImageFilterChannels.Blue) != 0)
					{
						if (_Kernels.HorizontalBlueKernel != null)
						{
							if (horzKernelSize >= 0 && horzKernelSize != _Kernels.HorizontalBlueKernel.Length)
								return false;
							horzKernelSize = _Kernels.HorizontalBlueKernel.Length;
						}
						if (_Kernels.VerticalBlueKernel != null)
						{
							if (vertKernelSize >= 0 && vertKernelSize != _Kernels.VerticalBlueKernel.Length)
								return false;
							vertKernelSize = _Kernels.VerticalBlueKernel.Length;
						}
					}
				}
				else
				{
					if (_Kernels.HorizontalLuminanceKernel != null)
						horzKernelSize = _Kernels.HorizontalLuminanceKernel.Length;
					if (_Kernels.VerticalLuminanceKernel != null)
						vertKernelSize = _Kernels.VerticalLuminanceKernel.Length;
					if (horzKernelSize >= 0 && vertKernelSize >= 0 && horzKernelSize != vertKernelSize)
						return false;
				}
			}

			//驗證影像大小
			IBitmapImage sourceBitmap = (this.SourceImage as IBitmapImage);
			if (sourceBitmap != null)
			{
				if ((horzKernelSize >= 0 && horzKernelSize > sourceBitmap.PixelWidth) || (vertKernelSize >= 0 && vertKernelSize > sourceBitmap.PixelHeight))
					return false;
			}

			//驗證完畢
			return true;
		}
        #endregion
	}
	/// <summary>
	/// 進行 Correlation 運算的 Separable Kernel 集合。
	/// </summary>
	public class SeparableCorrelationKernels : ICloneable
	{
		#region 建構子
		/// <summary>
		/// 初始化新的 SeparableCorrelationKernels 執行個體。
		/// </summary>
		public SeparableCorrelationKernels()
		{ }
		/// <summary>
		/// 初始化新的 SeparableCorrelationKernels 執行個體。
		/// </summary>
		/// <param name="template">現有的 SeparableCorrelationKernel。</param>
		public SeparableCorrelationKernels(SeparableCorrelationKernels template)
		{
			if (template != null)
			{
				//複製水平 Kernal
				double[] kernel = template.HorizontalAlphaKernel;
				if (kernel != null)
					this.HorizontalAlphaKernel = (kernel.Clone() as double[]);
				kernel = template.HorizontalBlueKernel;
				if (kernel != null)
					this.HorizontalBlueKernel = (kernel.Clone() as double[]);
				kernel = template.HorizontalGreenKernel;
				if (kernel != null)
					this.HorizontalGreenKernel = (kernel.Clone() as double[]);
				kernel = template.HorizontalLuminanceKernel;
				if (kernel != null)
					this.HorizontalLuminanceKernel = (kernel.Clone() as double[]);
				kernel = template.HorizontalRedKernel;
				if (kernel != null)
					this.HorizontalRedKernel = (kernel.Clone() as double[]);

				//複製垂直 Kernel
				kernel = template.VerticalAlphaKernel;
				if (kernel != null)
					this.VerticalAlphaKernel = (kernel.Clone() as double[]);
				kernel = template.VerticalBlueKernel;
				if (kernel != null)
					this.VerticalBlueKernel = (kernel.Clone() as double[]);
				kernel = template.VerticalGreenKernel;
				if (kernel != null)
					this.VerticalGreenKernel = (kernel.Clone() as double[]);
				kernel = template.VerticalLuminanceKernel;
				if (kernel != null)
					this.VerticalLuminanceKernel = (kernel.Clone() as double[]);
				kernel = template.VerticalRedKernel;
				if (kernel != null)
					this.VerticalRedKernel = (kernel.Clone() as double[]);
			}
		}
        #endregion
		#region Clone
		/// <summary>
		/// 建立深層副本。
		/// </summary>
		/// <returns>深層副本。</returns>
		public SeparableCorrelationKernels Clone()
		{
			return new SeparableCorrelationKernels(this);
		}
        #endregion
		#region HorizontalAlphaKernel
		/// <summary>
		/// 取得或設定套用至透明度的水平 Kernel，Null (Visual Basic 為 Nothing) 表示水平方向的透明度不套用濾鏡。
		/// </summary>
		public double[] HorizontalAlphaKernel { get; set; }
		#endregion
		#region HorizontalBlueKernel
		/// <summary>
		/// 取得或設定套用至藍色的水平 Kernel，Null (Visual Basic 為 Nothing) 表示水平方向的藍色不套用濾鏡。
		/// </summary>
		public double[] HorizontalBlueKernel { get; set; }
		#endregion
		#region HorizontalGreenKernel
		/// <summary>
		/// 取得或設定套用至綠色的水平 Kernel，Null (Visual Basic 為 Nothing) 表示水平方向的綠色不套用濾鏡。
		/// </summary>
		public double[] HorizontalGreenKernel { get; set; }
		#endregion
		#region HorizontalLuminanceKernel
		/// <summary>
		/// 取得或設定套用至光線強度的水平 Kernel，Null (Visual Basic 為 Nothing) 表示水平方向的光線強度不套用濾鏡。
		/// </summary>
		public double[] HorizontalLuminanceKernel { get; set; }
		#endregion
		#region HorizontalRedKernel
		/// <summary>
		/// 取得或設定套用至紅色的水平 Kernel，Null (Visual Basic 為 Nothing) 表示水平方向的紅色不套用濾鏡。
		/// </summary>
		public double[] HorizontalRedKernel { get; set; }
		#endregion
		#region ICloneable.Clone
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		#endregion
		#region SetArgbKernels
		/// <summary>
		/// 將透明度、藍色、綠色及紅色設定為同一組水平及垂直 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetArgbKernels(double[] kernel)
		{
			this.HorizontalAlphaKernel = kernel;
			this.VerticalAlphaKernel = kernel;
			this.SetRgbKernels(kernel);
		}
		#endregion
		#region SetHorizontalArgbKernels
		/// <summary>
		/// 將透明度、藍色、綠色及紅色設定為同一組水平 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetHorizontalArgbKernels(double[] kernel)
		{
			this.HorizontalAlphaKernel = kernel;
			this.SetHorizontalRgbKernels(kernel);
		}
		#endregion
		#region SetHorizontalRgbKernels
		/// <summary>
		/// 將藍色、綠色及紅色設定為同一組水平 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetHorizontalRgbKernels(double[] kernel)
		{
			this.HorizontalBlueKernel = kernel;
			this.HorizontalGreenKernel = kernel;
			this.HorizontalRedKernel = kernel;
		}
        #endregion
		#region SetRgbKernels
		/// <summary>
		/// 將藍色、綠色及紅色設定為同一組水平及垂直 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetRgbKernels(double[] kernel)
		{
			this.SetHorizontalRgbKernels(kernel);
			this.SetVerticalRgbKernels(kernel);
		}
		#endregion
		#region SetVerticalArgbKernels
		/// <summary>
		/// 將透明度、藍色、綠色及紅色設定為同一組垂直 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetVerticalArgbKernels(double[] kernel)
		{
			this.VerticalAlphaKernel = kernel;
			this.SetVerticalRgbKernels(kernel);
		}
		#endregion
		#region SetVerticalRgbKernels
		/// <summary>
		/// 將藍色、綠色及紅色設定為同一組垂直 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernal。</param>
		public void SetVerticalRgbKernels(double[] kernel)
		{
			this.VerticalBlueKernel = kernel;
			this.VerticalGreenKernel = kernel;
			this.VerticalRedKernel = kernel;
		}
		#endregion
		#region VerticalAlphaKernel
		/// <summary>
		/// 取得或設定套用至透明度的垂直 Kernel，Null (Visual Basic 為 Nothing) 表示垂直方向的透明度不套用濾鏡。
		/// </summary>
		public double[] VerticalAlphaKernel { get; set; }
		#endregion
		#region VerticalBlueKernel
		/// <summary>
		/// 取得或設定套用至藍色的垂直 Kernel，Null (Visual Basic 為 Nothing) 表示垂直方向的藍色不套用濾鏡。
		/// </summary>
		public double[] VerticalBlueKernel { get; set; }
		#endregion
		#region VerticalGreenKernel
		/// <summary>
		/// 取得或設定套用至綠色的垂直 Kernel，Null (Visual Basic 為 Nothing) 表示垂直方向的綠色不套用濾鏡。
		/// </summary>
		public double[] VerticalGreenKernel { get; set; }
		#endregion
		#region VerticalLuminanceKernel
		/// <summary>
		/// 取得或設定套用至光線強度的垂直 Kernel，Null (Visual Basic 為 Nothing) 表示垂直方向的光線強度不套用濾鏡。
		/// </summary>
		public double[] VerticalLuminanceKernel { get; set; }
		#endregion
		#region VerticalRedKernel
		/// <summary>
		/// 取得或設定套用至紅色的垂直 Kernel，Null (Visual Basic 為 Nothing) 表示垂直方向的紅色不套用濾鏡。
		/// </summary>
		public double[] VerticalRedKernel { get; set; }
		#endregion
	}
}
